#include <string.h>
#include "script_string.h"
#include "script_memory.h"
#include "script_debug.h"

char *uni_strip(char *buf)
{
	if(buf == NULL){
		return NULL;
	}
	int len = strlen(buf);
	if(len == 0){
		return buf;
	}

	len--;
	while( len >= 0 && 
			(buf[len] == '\n' ||
			buf[len] == '\r'))
	{
		buf[len] = 0;
		len -= 1;
	}
	
	while(buf[len] == ' ' ||
			buf[len] == '\t'){
		buf[len] = 0;
		len -= 1;
	}

	char *tmp;
	tmp = buf;
	while(*tmp == ' ' || *tmp == '\t'){
		tmp++;
	}

	return tmp;
}

inline int uni_is_number(char c)
{
	if(c >= '0' && c <= '9'){
		return 1;
	}
	return 0;
}

inline int uni_is_letter(unsigned char c)
{
	if( (c >= 'a' && c <= 'z') ||
			(c >= 'A' && c <= 'Z')||
			c == '_' ||
			c >= 0x80){
		return 1;
	}
	return 0;
}

int uni_is_alnum(char c)
{
	return uni_is_number(c) || uni_is_letter(c);
}

int uni_is_hex(char c)
{
	if( uni_is_number(c) || 
		(c >= 'a' && c <= 'f') ||
		(c >= 'A' && c <= 'F')){
		return 1;
	}
	return 0;
}

inline int uni_is_parenthesis(char c)
{
	if( c == '(' || c == ')' || c == ']'){
		return 1;
	}
	return 0;
}

#if 1
char *script_string_convert(const char *str)
{
	if(str == NULL){
		return NULL;
	}


	int len = strlen(str) + 1;
	if(len <= 0){
		return NULL;
	}

	char *new = (char*)script_calloc(1,len);
	if(new == NULL){
		return NULL;
	}

	int index = 0;
	char *tmp = (char*)str;
	while(*tmp){
		if(*tmp == '"' || *tmp == '\\'){
			new[index++] = '\\';
			new[index++] = *tmp;
		}else if(*tmp == '\n'){
			new[index++] = '\\';
			new[index++] = 'n';
		}else if(*tmp == '\r'){
			new[index++] = '\\';
			new[index++] = 'r';
		}else if(*tmp == '\t'){
			new[index++] = '\\';
			new[index++] = 't';
		}else if(*tmp == '\f'){
			new[index++] = '\\';
			new[index++] = 'f';
		}else{
			new[index++] = *tmp;
		}

		if(index >= len - 1){
			len += 32;
			char *tmp = script_calloc(1,len);
			memcpy(tmp,new,len-32);
			script_free(new);
			new = tmp;
		}

		tmp++;
	}

	return new;
}
#endif

char *script_string_disconvert(const char *str,int len)
{
	if(str == NULL){
		return NULL;
	}
	char *buf = script_calloc(1,len + 1);
	const char *tmp = str;
	char *new = buf;
	int i;
	for(i = 0; i < len && *tmp; i++){
		if(*tmp == '\\'){
			tmp++;
			switch(*tmp){
				case '"':
					*new++ = '"';
					break;
				case '\\':
					*new++ = '\\';
					break;
				case 'n':
					*new++ = '\n';
					break;
				case 't':
					*new++ = '\t';
					break;
				case 'r':
					*new++ = '\r';
					break;
				case 'f':
					*new++ = '\f';
					break;
				default:
					SCRIPT_DEBUG_ERR("String format error! '\\'");
					script_free(buf);
					return NULL;
					break;
			}
			tmp++;
			i++;
		}else{
			*new++ = *tmp++;
		}
	}
	return buf;
}


struct exp_string_t *exp_string_decode(char *str)
{
	if (str == NULL || (*str) == 0) {
		return NULL;
	}

	exp_string_type_e type = EXP_STRING_TYPE_NONE;
	struct exp_string_t *head = NULL, *node = NULL, *prev = NULL;

	char *point = str;
	char *prev_point = NULL; //仅用在const str 类型
	char *start = NULL;
	char *stop = NULL;

	char flag = 0;
	char string_flag = 0;	// const str 类型转码标记
	char done = 0;
	while (*point == ' ' || *point == '\t' || *point == '\r' || *point == '\n') {
		point++;
		continue;
	}

	while (1) {
		switch (type) {
			case EXP_STRING_TYPE_NONE:
				if (uni_is_number(*point)) {
					type = EXP_STRING_TYPE_NUMBER;
					start = point;
					flag = 0;
				} else if (uni_is_letter(*point)) {
					type = EXP_STRING_TYPE_STRING;
					start = point;
					flag = 0;
				} else if (uni_is_parenthesis(*point)) {
					type = EXP_STRING_TYPE_PARENTHESIS;
					start = point;
					flag = 1;
				} else if (*point == '"') {
					type = EXP_STRING_TYPE_STRING;
					start = point;
					string_flag = 1;
					flag = 1;
				} else if (*point == ' ' || *point == '\t' || *point == '\r' || *point == '\n') {
					point++;
					continue;
				} else if (*point == '-') {
					if(uni_is_number(*(point+1)) &&
							(prev == NULL || 
							prev->type == EXP_STRING_TYPE_OPERTOR ||
							prev->str[0] == '(' ||
							strcmp(prev->str,"return") == 0)){
						type = EXP_STRING_TYPE_NUMBER;
						start = point;
						flag = 0;
					}else{
						type = EXP_STRING_TYPE_OPERTOR;
						start = point;
					}
				} else {
					type = EXP_STRING_TYPE_OPERTOR;
					start = point;
				}
				break;
			case EXP_STRING_TYPE_NUMBER:
				switch (flag) {
					case 0:
						if (*point == 'x' || *point == 'X') {
							point++;
							flag = 1;
						} else if (*point == '.') {
							point++;
							flag = 2;
						} else if (!uni_is_number(*point)) {
							stop = point;
							done = 1;
						}
						break;
					case 1:
						if (!uni_is_hex(*point)) {
							stop = point;
							done = 1;
						}
						break;
					case 2:
						if (!uni_is_number(*point)) {
							stop = point;
							done = 1;
						}
						break;
				}
				break;
			case EXP_STRING_TYPE_STRING:
				switch (flag) {
					case 0:
						if (!uni_is_alnum(*point)) {
							stop = point;
							done = 1;
						}
						break;
					case 1:
						if ((*point == '"') && (*prev_point != '\\')) {
							point++;
							stop = point;
							done = 1;
						}
						break;
				}
				prev_point = point;
				break;
			case EXP_STRING_TYPE_OPERTOR:
				if((*start == '&' && *point == '&') ||
					(*start == '|' && *point == '|') ||
					(*start == '<' && *point == '=') ||
					(*start == '<' && *point == '<') ||
					(*start == '>' && *point == '=') ||
					(*start == '>' && *point == '>') ||
					((*start == '=' || *start == '!' || *start == '+' || *start == '-') 
					 && *point == '=' ) ||
					(*start == '=' && *point == '>')){
					point++;
					stop = point;
					
				}else{
					stop = point;
				}
				done = 1;
				break;
			case EXP_STRING_TYPE_PARENTHESIS:
				stop = point;
				done = 1;
				break;
		}

		//printf("start:%c point:%c %d type:%d done:%d\n",*start,*point,*point,type,done);

		if (done == 1) {
			node = script_calloc(1, sizeof(struct exp_string_t));
			node->type = type;

			if(type == EXP_STRING_TYPE_STRING && string_flag == 1){
				node->str = script_string_disconvert(start,stop-start);
				string_flag = 0;
			}else{
				node->str = script_calloc(1, stop - start + 2);
				strncpy(node->str, start, stop - start);
			}

			done = 0;
			type = EXP_STRING_TYPE_NONE;

			if (head == NULL) {
				head = node;
			} else {
				prev->next = node;
				node->prev = prev;
			}
			prev = node;
			point--;
		}

		if (*point == 0) {
			if(string_flag != 0){
				SCRIPT_DEBUG_ERR("'\"' doesn't paired!");
				exp_string_destroy(head);
				return NULL;
			}
			break;
		}

		point++;
	}

	//show_exp_string_t(head);
	return head;
}

struct exp_string_t *exp_lines_decode(char *str)
{
	exp_string_t *lines = NULL;
	exp_string_t *line = NULL;
	char *start = (char*)str;
	while(*start != 0){
		char *ss;
		char *end = strchr(start,'\n');
		if(end){
			ss = script_calloc(1,end-start+1);
			strncpy(ss,start,end-start);
		}else{
			ss = script_calloc(1,strlen(start)+1);
			strcpy(ss,start);
		}
		uni_strip(ss);
		if(strlen(ss) == 0){
			if(end == NULL){
				break;
			}
			start = end + 1;
			continue;
		}

		line = script_calloc(1,sizeof(exp_string_t));
		line->str = ss;
		if(lines == NULL){
			lines = line;
		}else{
			exp_string_t *t = lines;
			while(t->next){
				t = t->next;
			}
			t->next = line;
		}

		if(end == NULL){
			break;
		}
		start = end+1;
	}
	return lines;
}


void exp_string_destroy(exp_string_t *exp_str)
{
	exp_string_t *tmp = exp_str;
	exp_string_t *next;
	while(tmp){
		next = tmp->next;
		if(tmp->str){
			script_free(tmp->str);
		}
		script_free(tmp);
		tmp = next;
	}
}

char *script_string_dump(const char *src)
{
	if(src == NULL){
		return NULL;
	}

	char *dst = script_calloc(1,strlen(src)+1);
	if(!dst){
		return NULL;
	}
	strcpy(dst,src);
	return dst;
}

